/**
 * BogusAdvertiser.java
 *
 * @author BJ Premore
 */


package SSF.OS.BGP4.Widgets;


import java.util.*;
import com.renesys.raceway.SSF.*;
import com.renesys.raceway.DML.*;
import SSF.Net.*;
import SSF.OS.*;
import SSF.OS.BGP4.*;
import SSF.OS.BGP4.Comm.*;
import SSF.OS.BGP4.Timing.*;
import SSF.OS.BGP4.Path.*;
import SSF.OS.BGP4.Util.*;


// ===== class SSF.OS.BGP4.Widgets.BogusAdvertiser ========================= //
/**
 * A test class, masquerading as a ProtocolSession, for injecting a given
 * route into a system, and later optionally withdrawing it.
 */
public class BogusAdvertiser extends ProtocolSession {

  // ........................ member data .......................... //

  /** The BGPSession with which this tester is associated. */
  private BGPSession bgp;

  /** Whether or not the setup work has been done yet. */
  private boolean setup = false;

  /** The amount of time, in seconds, to wait from the beginning of the
   *  simulation until sending out the advertisement for the given route. */
  private double wait1 = -1.0;

  /** The IP address of the route to advertise. */
  private IPaddress thedest = null;

  /** The amount of time, in seconds, to wait after the advertisement of the
   *  given route before withdrawing that route.  If the value is 0, there will
   *  be no withdrawal. */
  private double wait2 = -1.0;

  /** Whether or not to randomize the order in which the peers of the local
   *  router are sent the advertisement and withdrawal of the destination. */
  private boolean randomize_peers = false;

  /** A temporary string for holding the NHI address of the route to be
   *  advertised until it can be converted to an IP address. */
  private String tmpstr;

  // ----- BogusAdvertiser() ----------------------------------------------- //
  /**
   * We must have a zero-argument, public constructor (so that
   * <code>newInstance()</code> can be used to create a new
   * <code>BogusAdvertiser</code> instance).
   */
  public BogusAdvertiser() {
  }

  // ----- config ---------------------------------------------------------- //
  /**
   * Set the configurable values (specified in DML file).
   *
   * @exception configException  if any of the calls to <code>find</code>
   *                             or <code>findSingle</code> throw such an
   *                             exception.
   */
  public void config(Configuration cfg) throws configException {
    super.config(cfg);

    String str;
    str = (String)cfg.findSingle("wait1");
    if (str == null) {
      System.out.println("BogusAdvertiser missing wait1 attribute");
    } else {
      wait1 = Double.valueOf(str).doubleValue();
    }

    str = (String)cfg.findSingle("dest");
    if (str == null) {
      System.out.println("BogusAdvertiser missing dest attribute");
    } else {
      if (str.equals("bogus")) {
        thedest = Debug.bogusip;
      } else {
        tmpstr = str;
      }
    }

    str = (String)cfg.findSingle("wait2");
    if (str == null) {
      System.out.println("BogusAdvertiser missing wait2 attribute");
    } else {
      wait2 = Double.valueOf(str).doubleValue();
    }

    str = (String)cfg.findSingle("randomize_peers");
    if (str != null) {
      randomize_peers = Boolean.valueOf(str).booleanValue();
    }
  }

  // ----- init ------------------------------------------------------------ //
  /**
   * Creates an SSF process which sends a message advertising a given
   * destination after a given startup wait period, later optionally
   * withdrawing the route.
   */
  public void init() {
    new process(inGraph()) {
        public boolean isSimple() {
          return false;
        }

        public void action() {
          if (!setup) {
            try {
              bgp = (BGPSession)inGraph().SessionForName("bgp");
            } catch (ProtocolException pex) {
              throw new Error("BogusAdvertiser couldn't get handle to BGP");
            }

            if (thedest == null) {
              String tmpip = bgp.topnet.nhi_to_ip(tmpstr);
              // Bug in nhi_to_ip does not return /32 as prefix length of
              // interface addresses, so we fix it here.
              if (tmpstr.indexOf("(") >= 0) {
                System.out.println("fixing /32 bug");
                tmpip = tmpip.substring(0,tmpip.length()-2) + "32";
              }
              thedest = new IPaddress(tmpip);
	    }

            setup = true;
          } else {
            waitFor(Net.seconds(wait1));

            // We compose an update message to advertise the given route to
            // each of our neighbors.

            PeerEntry[] peers = new PeerEntry[bgp.nbs.length-1];
            ArrayList peerlist = new ArrayList();
            
            for (int i=0; i<bgp.nbs.length-1; i++) { // skip last nb ('self')
              peerlist.add(bgp.nbs[i]);
            }

            if (randomize_peers) {
              System.out.println("using randomized peers");
              for (int i=peers.length; i>0; i--) {
                int index = ((int)(bgp.rng2.nextDouble()*Integer.MAX_VALUE))%i;
                peers[peers.length-i] = (PeerEntry)peerlist.remove(index);
              }
            } else {
              for (int i=0; i<peers.length; i++) {
                peers[i] = (PeerEntry)peerlist.remove(0);
              }
            }

            //System.out.println("the peer order:");
            //for (int i=0; i<bgp.nbs.size(); i++) {
            //  System.out.println("    ... " + peers[i].nh);
            //}

            for (int i=0; i<peers.length; i++) {
              Route rte = new Route();
              rte.set_nlri(new IPaddress(thedest));
              if (!Global.basic_attribs) {
                rte.set_origin(Origin.IGP);
              }
              rte.prepend_as(bgp.as_nh);
              rte.set_nexthop(peers[i].return_ip);
              // send message
              UpdateMessage msg = new UpdateMessage(bgp.nh, rte);
              System.out.println("**:");
              System.out.println(peers[i].bgp_id);
              bgp.force_send(msg, peers[i], 0);
              bgp.reset_timer(peers[i], SSF.OS.BGP4.Timing.Timer.KEEPALIVE);
              bgp.reset_timer(peers[i], SSF.OS.BGP4.Timing.Timer.HOLD);
            }

            if (wait2 == 0.0) {
              waitForever();
            }

            waitFor(Net.seconds(wait2));

            // We compose an update message to withdraw the same route from
            // each of our neighbors.
            for (int i=0; i<peers.length; i++){
              UpdateMessage msg = new UpdateMessage(bgp.nh);
              msg.add_wd(thedest);
              bgp.force_send(msg, peers[i], 0);
              bgp.reset_timer(peers[i], SSF.OS.BGP4.Timing.Timer.KEEPALIVE);
              bgp.reset_timer(peers[i], SSF.OS.BGP4.Timing.Timer.HOLD);
            }

            waitForever();
            
          }
        }
      };
  }

  // ----- now ------------------------------------------------------------- //
  /**
   * Returns the current simulation time.  A convenience method so that
   * functions, not just Processes, can get the current simulation time.
   *
   * @return the current simulation time
   */
  public double now() {
    return ((double)(inGraph().now()))/((double)Net.frequency);
  }

  // ----- push ------------------------------------------------------------ //
  /**
   * Handles incoming events.
   */
  public boolean push(ProtocolMessage message, ProtocolSession fromSession) {
    // there will never be any incoming events in this pseudo-ProtocolSession
    throw new Error("BogusAdvertiser.push() called");
  }

} // end of class BogusAdvertiser
